package codetail.graphics.drawables;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Component;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.NotExistException;
import ohos.global.resource.ResourceManager;
import ohos.global.resource.WrongTypeException;
import ohos.global.resource.solidxml.Attribute;
import ohos.global.resource.solidxml.SolidXml;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.io.IOException;
import java.util.List;

public class LollipopDrawable extends ShapeElement implements Component.DrawTask,
        Component.TouchEventListener, Component.LongClickedListener, Component.ClickedListener {
    private static final float DEFAULT_DIAMETER_DP = 80;
    private static final int DEFAULT_OUTER_LINE_COLOR = 0x1A000000;
    private static final int DEFAULT_CIRCLE_COLOR = Color.getIntColor("#00000000");
    private static final float DEFAULT_ALPHA = 0.4f;
    private static final int DEFAULT_DURATION = 350;
    private static final int ADD_RADIUS_LENGTH = 200;
    private static final int ADD_POSITION_Y = 50;
    private static final int HOVER_DURATION = 1000;
    private int rippleColor = Color.rgb(0, 0, 0);
    private Component target;
    private final Paint mPaint = new Paint();
    private MmiPoint pointerPosition;
    private float xAxis;
    private float yAxis;
    private float mRadiusRipple;
    private float rippleDiameter = DEFAULT_DIAMETER_DP;
    private ShapeElement mOuterLineDrawable;
    private boolean mFingerUp = true;
    private EventHandler handler;
    private DelayRunAnimator delayRunAnimator = new DelayRunAnimator();
    private int rippleDuration;
    private float maxRadius;
    private boolean isLongClick = false;
    private AnimatorValue animatorValue;
    private final AnimatorValue.ValueUpdateListener mAnimatorUpdateListener = new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            if (v <= 0) {
                return;
            }
            float currentRadius = rippleDiameter + maxRadius * v;
            if (currentRadius < rippleDiameter) {
                setRadius(rippleDiameter);
            } else {
                setRadius(currentRadius);
            }
            target.invalidate();
        }
    };

    /**
     * 获取背景
     *
     * @param component component
     * @param shapeResource shapeResource
     * @return shapeElement
     */
    public Element getShapeElement(Component component, int shapeResource) {
        initRipple(component, shapeResource);
        return component.getBackgroundElement();
    }

    private void initRipple(Component component, int shapeResource) {
        this.target = component;
        ResourceManager resourceManager = component.getContext().getResourceManager();
        try {
            SolidXml solidXml = resourceManager.getSolidXml(shapeResource);
            if (solidXml.getRoot() != null) {
                if (solidXml.getRoot().getChild() != null) {
                    List<Attribute> attribute = solidXml.getRoot().getChild().getAttributes();
                    if (attribute.size() > 0) {
                        for (int i = 0; i < attribute.size(); i++) {
                            if (attribute.get(i).getName().equals("color")) {
                                String mColor = attribute.get(i).getStringValue();
                                rippleColor = Color.getIntColor(mColor);
                            }
                        }
                    }
                }
            }

        } catch (IOException | NotExistException | WrongTypeException e) {
            e.printStackTrace();
        }

        handler = new EventHandler(EventRunner.getMainEventRunner());
        setupPaint();
        target.setClickedListener(this);
        target.setLongClickedListener(this);
        component.addDrawTask(this);
        component.setTouchEventListener(this);
    }

    private ShapeElement generateTransparentBackgroundDrawable() {
        ShapeElement element = new ShapeElement();
        element.setRgbColor(RgbColor.fromArgbInt(Color.TRANSPARENT.getValue()));
        return element;
    }

    private ShapeElement generateBackgroundDrawable() {
        ShapeElement element = new ShapeElement();
        element.setRgbColor(RgbColor.fromArgbInt(rippleColor));
        element.setAlpha(95);
        return element;
    }

    /**
     * 初始化画笔
     */
    private void setupPaint() {
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setColor(new Color(DEFAULT_CIRCLE_COLOR));
        mPaint.setAlpha(0.0f);
    }

    /**
     * set Ripple color
     *
     * @param rippleColor color
     */
    public void setRippleColor(int rippleColor) {
        mPaint.setColor(new Color(rippleColor));
        mPaint.setAlpha(DEFAULT_ALPHA);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mOuterLineDrawable != null) {
            mOuterLineDrawable.drawToCanvas(canvas);
        }
        canvas.drawCircle(getX(), getY(), getRadius(), mPaint);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        touchEvent.getRadius(0);
        pointerPosition = touchEvent.getPointerPosition(0);

        float xAxis = pointerPosition.getX();
        float yAxis = pointerPosition.getY();

        int[] parentLocationOnScreen = component.getLocationOnScreen();

        setX(xAxis - parentLocationOnScreen[0]);
        int halfHeight = component.getHeight() / 2;
        if (yAxis > halfHeight) {
            setY(yAxis - ADD_POSITION_Y);
        } else {
            setY(yAxis + ADD_POSITION_Y);
        }
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                setRippleColor(rippleColor);
                mOuterLineDrawable = generateTransparentBackgroundDrawable();
                mFingerUp = false;
                handler.postTask(delayRunAnimator, 200);
                setRippleDuration(rippleDuration);
                if (Math.abs(xAxis - parentLocationOnScreen[0]) > Math.abs(xAxis - (parentLocationOnScreen[0] + component.getWidth()))) {
                    maxRadius = Math.abs(xAxis - parentLocationOnScreen[0]) + ADD_RADIUS_LENGTH;
                } else {
                    maxRadius = Math.abs(xAxis - (parentLocationOnScreen[0] + component.getWidth())) + ADD_RADIUS_LENGTH;
                }
                mRadiusRipple = maxRadius;
                resetConfig();
                return true;
            case TouchEvent.POINT_MOVE:
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                mFingerUp = true;
                if (!isLongClick) {
                    handler.removeTask(delayRunAnimator);
                    if (rippleDuration <= 0) {
                        rippleDuration = DEFAULT_DURATION;
                    }
                    setRippleDuration(rippleDuration);
                    initAnimator();
                }
                if (isLongClick) {
                    fingerUp();
                }
                break;
            default:
                break;
        }
        return false;
    }

    /**
     * 获取x
     *
     * @return x_axis
     */
    public float getX() {
        return xAxis;
    }

    /**
     * xAxis赋值
     *
     * @param xAxis xAxis
     */
    public void setX(float xAxis) {
        this.xAxis = xAxis;
    }

    /**
     * 获取y
     *
     * @return yAxis
     */
    public float getY() {
        return yAxis;
    }

    /**
     * yAxis赋值
     *
     * @param yAxis yAxis
     */
    public void setY(float yAxis) {
        this.yAxis = yAxis;
    }

    /**
     * getRadius
     *
     * @return float
     */
    public float getRadius() {
        return mRadiusRipple;
    }

    /**
     * setRadius
     *
     * @param radius
     */
    public void setRadius(float radius) {
        this.mRadiusRipple = radius;
    }

    private void resetConfig() {
        setRadius(getRadius());
    }

    @Override
    public void onLongClicked(Component component) {
    }

    @Override
    public void onClick(Component component) {
    }

    private class DelayRunAnimator implements Runnable {
        @Override
        public void run() {
            mOuterLineDrawable = generateBackgroundDrawable();
            isLongClick = true;
            initLongClickAnimator();
        }
    }

    public void setRippleDuration(int rippleDuration) {
        this.rippleDuration = rippleDuration;
    }

    /**
     * 抬起
     */
    private void fingerUp() {
        if (mFingerUp) {
            setRadius(0);
            target.invalidate();
        }
        isLongClick = false;
    }

    private void initAnimator() {
        animatorValue = new AnimatorValue();
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animatorValue.setDuration(rippleDuration);
        animatorValue.setValueUpdateListener(mAnimatorUpdateListener);
        animatorValue.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                mOuterLineDrawable = generateTransparentBackgroundDrawable();
                setRadius(0);
                fingerUp();
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animatorValue.start();
    }

    /**
     * 长按事件动画
     */
    private void initLongClickAnimator() {
        animatorValue = new AnimatorValue();
        animatorValue.setCurveType(Animator.CurveType.LINEAR);
        animatorValue.setDuration(HOVER_DURATION);
        animatorValue.setValueUpdateListener(mAnimatorUpdateListener);
        animatorValue.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                mOuterLineDrawable = generateTransparentBackgroundDrawable();
                if (mFingerUp) {
                    setRadius(0);
                    target.invalidate();
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animatorValue.start();
    }
}
